package com.budwk.app.util.poiUtils;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.budwk.app.oss.services.SysOssService;
import com.budwk.app.sqfw.enums.StreetEnum;
import com.budwk.app.util.ThreadPoolExecutorUtil;
import com.budwk.app.util.poiUtils.nutzBeanUtils.NutzIocUtils;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.budwk.app.util.poiUtils.CommonFormOfficeUtils.getTemplatePathByOS;

/**
 * @author yangweimin
 * @project wk-mini
 * @description 生成字段换行图片
 * @date 2023/10/14 00:26:53
 */
@Slf4j
public class PictureUtils {
    private static final Integer ZERO = 0;
    private static final Integer ONE_ZERO_TWO_FOUR = 1024;
    private static final Integer NINE_ZERO_ZERO = 900;
    private static final Integer THREE_TWO_SEVEN_FIVE = 3275;
    private static final Integer TWO_ZERO_FOUR_SEVEN = 2047;
    private static final Double ZERO_EIGHT_FIVE = 0.85;
    private static final Double ZERO_SIX = 0.6;
    private static final Double ZERO_FOUR_FOUR = 0.44;
    private static final Double ZERO_FOUR = 0.4;

    private static SysOssService sysOssService = (SysOssService) NutzIocUtils.getBeanByType(SysOssService.class);

    public static ByteArrayOutputStream getByteArrayOutputStream(String title, List<String> urls, String templateName) {
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        Map<String, Object> datas = new HashMap<>();
        datas.put("title", title);
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        List<String> pictureTag = new ArrayList<>();
        for (String url : urls) {
            Map<String, Object> detailMap = new HashMap<String, Object>();//将word中标签名的例和对应数据保存到map
            detailMap.put("picture", CommonFormOfficeUtils.addWordPicture(url, 10F));
            pictureTag.add("picture");//用于设置图片
            list.add(detailMap);//将设置好的行保存到list集合中
        }
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder().bind("pictures", policy).build();//设置列表配置，如果有多个列表时需加.bind("list1", policy) 新列表配置即可
        for (String tag:pictureTag) {
            config.customPolicy(tag,new PictureRenderPolicy());//设置图片，不然保存的是一串字符
        }
        //将列表保存到渲染的map中
        datas.put("pictures", list);
        //获取模板
        String filePathStr = getTemplatePathByOS(templateName);
        //3.创建XWPFTemplate对象，并设置读取模板路径和要渲染的数据
        XWPFTemplate template = XWPFTemplate.compile(filePathStr, config).render(datas);
        try {
            template.write(wordOutPutStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return wordOutPutStream;
    }

    public static ByteArrayOutputStream getByteArrayOutputStream(String title, String c_title, List<String> pictures,
                                                                 List<String> c_pictures, String templateName) {
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        Map<String, Object> datas = new HashMap<>();
        datas.put("title", title);
        datas.put("c_title", c_title);
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        List<String> pictureTag = new ArrayList<>();
        for (String url : pictures) {
            Map<String, Object> detailMap = new HashMap<String, Object>();//将word中标签名的例和对应数据保存到map
            detailMap.put("picture", CommonFormOfficeUtils.addWordPicture(url, 10F));
            pictureTag.add("picture");//用于设置图片
            list.add(detailMap);//将设置好的行保存到list集合中
        }
        List<Map<String, Object>> c_list = new ArrayList<Map<String, Object>>();
        List<String> c_pictureTag = new ArrayList<>();
        for (String url : c_pictures) {
            Map<String, Object> detailMap = new HashMap<String, Object>();//将word中标签名的例和对应数据保存到map
            detailMap.put("picture", CommonFormOfficeUtils.addWordPicture(url, 10F));
            c_pictureTag.add("picture");//用于设置图片
            c_list.add(detailMap);//将设置好的行保存到list集合中
        }
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        //设置列表配置，如果有多个列表时需加.bind("list1", policy) 新列表配置即可
        Configure config = Configure.builder().bind("pictures", policy).bind("c_pictures", policy).build();
        for (String tag : pictureTag) {
            config.customPolicy(tag,new PictureRenderPolicy());//设置图片，不然保存的是一串字符
        }
        for (String tag : c_pictureTag) {
            config.customPolicy(tag,new PictureRenderPolicy());//设置图片，不然保存的是一串字符
        }
        //将列表保存到渲染的map中
        datas.put("pictures", list);
        datas.put("c_pictures", c_list);
        //获取模板
        String filePathStr = getTemplatePathByOS(templateName);
        //3.创建XWPFTemplate对象，并设置读取模板路径和要渲染的数据
        XWPFTemplate template = XWPFTemplate.compile(filePathStr, config).render(datas);
        try {
            template.write(wordOutPutStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return wordOutPutStream;
    }

    /**
     * 图片缩放，旋转(图片宽度固定宽度)
     * 一行word正好可以装3张宽度为180的图片
     * @param imgPath
     * @return
     * @throws IOException
     */
    public static PictureRenderData scaleImage(String imgPath, int width, boolean isRotate) {
        if (StrUtil.isBlank(imgPath)) return null;
        InputStream inputStream = null;
        ByteArrayOutputStream bos = null;
        try {
            inputStream = CommonFormOfficeForAdminUtils.downloadPictureCache(imgPath);
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            //如果是横图，就旋转90度
            if (bufferedImage.getWidth() > bufferedImage.getHeight() && isRotate) {
                bufferedImage = (BufferedImage) ImgUtil.rotate(bufferedImage, 90);
            }

            int w = bufferedImage.getWidth();
            int h = bufferedImage.getHeight();
            float f = width / Float.parseFloat(w+"");
            int height = (int) (h * f);
            //缩放
            Image image = ImgUtil.scale(bufferedImage, width, height);
            //这里的Image不能强制转成BufferedImage，会报错，只能通过下面的方式来转
            BufferedImage scaleImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = scaleImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            //BufferedImage转byte[]
            bos = new ByteArrayOutputStream();
            String formatName = imgPath.substring(imgPath.lastIndexOf(".") + 1);
            formatName = "jpg".equals(formatName) ? "png" : formatName;
            ImageIO.write(scaleImage, formatName, bos); // 如果是jpg图片，这里转换会为空字符
            return Pictures.ofBytes(bos.toByteArray()).create();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }

    /**
     * 图片缩放，旋转(图片宽度固定宽度)
     * 一行word正好可以装3张宽度为180的图片
     * @param imgPath
     * @return
     * @throws IOException
     */
    public static PictureRenderData scaleImage(String imgPath, int width, int height, boolean isRotate) {
        if (StrUtil.isBlank(imgPath)) return null;
        InputStream inputStream = null;
        ByteArrayOutputStream bos = null;
        try {
            inputStream = CommonFormOfficeForAdminUtils.downloadPictureCache(imgPath);
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            //如果是横图，就旋转90度
            if (bufferedImage.getWidth() > bufferedImage.getHeight() && isRotate) {
                bufferedImage = (BufferedImage) ImgUtil.rotate(bufferedImage, 90);
            }

            int w = bufferedImage.getWidth();
            int h = bufferedImage.getHeight();
            float f = width / Float.parseFloat(w+"");
            h = (int) (h * f);
            if (h < height) {
                height = h;
            }
            //缩放
            Image image = ImgUtil.scale(bufferedImage, width, height);
            //这里的Image不能强制转成BufferedImage，会报错，只能通过下面的方式来转
            BufferedImage scaleImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = scaleImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            //BufferedImage转byte[]
            bos = new ByteArrayOutputStream();
            String formatName = imgPath.substring(imgPath.lastIndexOf(".") + 1);
            formatName = "jpg".equals(formatName) ? "png" : formatName;
            ImageIO.write(scaleImage, formatName, bos); // 如果是jpg图片，这里转换会为空字符
            return Pictures.ofBytes(bos.toByteArray()).create();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }

    public static List<byte[]> scaleImageToByte(String imgPath, int size, boolean isRotate) {
        if (StringUtils.isBlank(imgPath)) return null;
        List<byte[]> bytes = new ArrayList<>();
        String  urls [] = imgPath.split(",");
        for (int i = 0; i < (urls.length < size ? urls.length : size); i ++) {
            if (StrUtil.isBlank(urls[i])) continue;
            InputStream inputStream = null;
            try {
                String url = urls[i];
                inputStream = CommonFormOfficeForAdminUtils.downloadPictureCache(url);
                BufferedImage bufferedImage = ImageIO.read(inputStream);
                //如果是横图，就旋转90度
                if (bufferedImage.getWidth() > bufferedImage.getHeight() && isRotate) {
                    bufferedImage = (BufferedImage) ImgUtil.rotate(bufferedImage, 90);
                }

                // 获取图片后缀
                String formatName = url.substring(url.lastIndexOf(".") + 1);
                formatName = "jpg".equals(formatName) ? "png" : formatName;

                // 不压缩图片
                byte[] imgBytes = bufferedImageToByte(bufferedImage, formatName);

                // 图片压缩
                //byte[] imgBytes = compressPicForScale(bufferedImage, 100, formatName);

                bytes.add(imgBytes);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return bytes;
    }

    /**
     * 签名
     * 一次只能签一个名，有多个签名，必须多次调用
     * @param xwpfDocument
     * @param sign
     * @param path
     * @param leftOffset
     * @param topOffset
     * @param width
     * @return
     */
    public static NiceXWPFDocument networkImageSeal(NiceXWPFDocument xwpfDocument, String sign, String path,
                                                    Integer leftOffset, Integer topOffset, Integer width) {
        InputStream inputStream = null;
        if (StringUtils.isBlank(path)) return xwpfDocument;
        try {
            inputStream = CommonFormOfficeForAdminUtils.downloadPictureCache(path);
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            int w = bufferedImage.getWidth();
            int h = bufferedImage.getHeight();
            float f = width / Float.parseFloat(w+"");
            int height = (int) (h * f);
            if (h > w) {
                height = 40;
                f = height / Float.parseFloat(h+"");
                width = (int) (w * f);
            }

            if (topOffset == null) {
                topOffset = 0;
            }
            InputStream inputStream1 = inputStream = bufferedImageToInputStream(bufferedImage, path);
            return seal(xwpfDocument, sign, inputStream1, leftOffset, topOffset, width, height);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return xwpfDocument;
    }

    public static NiceXWPFDocument relPathImageSeal(NiceXWPFDocument xwpfDocument, String sign, String path,
                                                    Integer leftOffset, Integer topOffset, Integer width) {
        InputStream inputStream = null;
        if (StringUtils.isBlank(path)) return xwpfDocument;
        try {
            String abPath = getTemplatePathByOS(path);
            inputStream = new FileInputStream(abPath);
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            int w = bufferedImage.getWidth();
            int h = bufferedImage.getHeight();
            float f = width / Float.parseFloat(w+"");
            int height = (int) (h * f);
            if (h > w) {
                height = 40;
                f = height / Float.parseFloat(h+"");
                width = (int) (w * f);
            }

            if (topOffset == null) {
                topOffset = 0;
            }
            InputStream inputStream1 = inputStream = bufferedImageToInputStream(bufferedImage, path);
            return seal(xwpfDocument, sign, inputStream1, leftOffset, topOffset, width, height);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return xwpfDocument;
    }

    /**
     * 本地图片盖章
     * 一次只能盖一个章，有多个盖章，必须多次调用
     * @param xwpfDocument
     * @param sign
     * @param path
     * @param leftOffset
     * @param topOffset
     * @param width
     * @return
     */
    public static NiceXWPFDocument localImageSeal(NiceXWPFDocument xwpfDocument, String sign, String path,
                                                  Integer leftOffset, Integer topOffset, Integer width) {
        if (StringUtils.isBlank(path)) return xwpfDocument;
        try {
            BufferedImage bufferedImage = ImageIO.read(new File(path));
            int w = bufferedImage.getWidth();
            int h = bufferedImage.getHeight();
            float f = width / Float.parseFloat(w+"");
            int height = (int) (h * f);
            if (h > w) {
                height = width;
                f = height / Float.parseFloat(h+"");
                width = (int) (w * f);
            }

            if (topOffset == null) {
                topOffset = 0;
            }
            InputStream inputStream1 = bufferedImageToInputStream(bufferedImage, path);
            return seal(xwpfDocument, sign, inputStream1, leftOffset, topOffset, width, height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return xwpfDocument;
    }

    /**
     * 盖章/签名
     * @param xwpfDocument
     * @param sign
     * @param leftOffset
     * @param topOffset
     * @param width
     * @return
     */
    public static NiceXWPFDocument seal(NiceXWPFDocument xwpfDocument, String sign, InputStream inputStream,
                                        Integer leftOffset, Integer topOffset, Integer width, Integer height) {
        try {
            //对普通段落内容进行匹配
            List<XWPFParagraph> xwpfParagraphList = xwpfDocument.getParagraphs();
            for (XWPFParagraph x : xwpfParagraphList) {
                String text = x.getText();
                //找到需要盖章的标记
                if (text.contains(sign)) {
                    //签章
                    XWPFParagraph paragraph = x;
                    if (paragraph != null) {
                        //添加印章图片
                        XWPFRun targetRun = paragraph.createRun();
                        targetRun.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_JPEG, "sign", Units.toEMU(100), Units.toEMU(100));
                        CTR targetRunCtr = targetRun.getCTR();
                        if (targetRunCtr.sizeOfDrawingArray() > 0) {
                            CTDrawing drawing = targetRunCtr.getDrawingArray(0);
                            if (drawing.sizeOfInlineArray() > 0) {
                                CTGraphicalObject graphic = drawing.getInlineArray(0).getGraphic();
                                //拿到新插入的图片替换添加CTAnchor 设置浮动属性 删除inline属性
                                CTAnchor anchor = getAnchorWithGraphic(graphic, "Seal" + System.currentTimeMillis(),
                                        Units.toEMU(width), Units.toEMU(height),
                                        Units.toEMU(leftOffset), Units.toEMU(topOffset), false);
                                drawing.setAnchorArray(new CTAnchor[]{anchor});
                                drawing.removeInline(0);
                            }
                        }
                        return xwpfDocument;
                    }
                }
            }

            // 对表格内容进行匹配
            List<XWPFTable> xwpfTables = xwpfDocument.getTables();
            if(CollectionUtils.isNotEmpty(xwpfTables)) {
                for (XWPFTable xwpfTable : xwpfTables) {
                    if (null != xwpfTable) {
                        for (XWPFTableRow xwpfTableRow : xwpfTable.getRows()) {
                            if (null != xwpfTableRow) {
                                for (XWPFTableCell xwpfTableCell : xwpfTableRow.getTableCells()) {
                                    if (null != xwpfTableCell) {
                                        for (XWPFParagraph x : xwpfTableCell.getParagraphs()) {
                                            String text = x.getText();
                                            //找到需要盖章的标记
                                            if (text.contains(sign)) {
                                                //签章
                                                XWPFParagraph paragraph = x;
                                                if (paragraph != null) {
                                                    //添加印章图片
                                                    XWPFRun targetRun = paragraph.createRun();
                                                    targetRun.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_JPEG, "sign", Units.toEMU(100), Units.toEMU(100));
                                                    CTR targetRunCtr = targetRun.getCTR();
                                                    if (targetRunCtr.sizeOfDrawingArray() > 0) {
                                                        CTDrawing drawing = targetRunCtr.getDrawingArray(0);
                                                        if (drawing.sizeOfInlineArray() > 0) {
                                                            CTGraphicalObject graphic = drawing.getInlineArray(0).getGraphic();
                                                            //拿到新插入的图片替换添加CTAnchor 设置浮动属性 删除inline属性
                                                            CTAnchor anchor = getAnchorWithGraphic(graphic, "Seal" + System.currentTimeMillis(),
                                                                    Units.toEMU(width), Units.toEMU(height),
                                                                    Units.toEMU(leftOffset), Units.toEMU(topOffset), false);
                                                            drawing.setAnchorArray(new CTAnchor[]{anchor});
                                                            drawing.removeInline(0);
                                                        }
                                                    }
                                                    return xwpfDocument;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return xwpfDocument;
    }

    /**
     * @param ctGraphicalObject 图片数据
     * @param deskFileName      图片描述
     * @param width             宽
     * @param height            高
     * @param leftOffset        水平偏移 left
     * @param topOffset         垂直偏移 top
     * @param behind            文字上方/文字下方(false:章盖在文字上面，不会被文字挡住，true:章盖在文字下面，被文字挡住)
     * @return
     * @throws Exception
     */
    private static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject,
                                                 String deskFileName, int width, int height,
                                                 int leftOffset, int topOffset, boolean behind) {
        String anchorXML =
                "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
                        + "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
                        + "<wp:simplePos x=\"0\" y=\"0\"/>"
                        + "<wp:positionH relativeFrom=\"column\">"
                        + "<wp:posOffset>" + leftOffset + "</wp:posOffset>"
                        + "</wp:positionH>"
                        + "<wp:positionV relativeFrom=\"paragraph\">"
                        + "<wp:posOffset>" + topOffset + "</wp:posOffset>" +
                        "</wp:positionV>"
                        + "<wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
                        + "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
                        + "<wp:wrapNone/>"
                        + "<wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" + deskFileName + "\"/><wp:cNvGraphicFramePr/>"
                        + "</wp:anchor>";

        CTDrawing drawing = null;
        try {
            drawing = CTDrawing.Factory.parse(anchorXML);
        } catch (XmlException e) {
            e.printStackTrace();
        }
        CTAnchor anchor = drawing.getAnchorArray(0);
        anchor.setGraphic(ctGraphicalObject);
        return anchor;
    }

    private static InputStream bufferedImageToInputStream(BufferedImage bufferedImage, String path) throws IOException {
        //ByteArrayOutputStream是内存读写流，不同于指向硬盘的流，
        // 它内部是使用字节数组读内存的，这个字节数组是它的成员变量，
        // 当这个数组不再使用变成垃圾的时候，Java的垃圾回收机制会将它回收
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        String formatName = path.substring(path.lastIndexOf(".") + 1);
        formatName = "jpg".equals(formatName) ? "png" : formatName;
        ImageIO.write(bufferedImage, formatName, os);
        return new ByteArrayInputStream(os.toByteArray());
    }

    public static void close(XWPFTemplate xwpfTemplate, NiceXWPFDocument xwpfDocument) {
        try {
            if (xwpfDocument != null) {
                xwpfDocument.close();
            }
            if (xwpfTemplate != null) {
                xwpfTemplate.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取本地照片的完整路径
     * @param unitName
     * @return
     */
    public static String getLocalImagePath(String unitName) {
        String catalogue = null;

        //获取中文首字母
        String unitNameChar = null;

        //威清门街道
        if (unitName.contains(StreetEnum.WEI_QING_MEN_STREET.getName())) {
            catalogue = "weiQingMen/picture/";
            unitNameChar = PinyinUtil.getFirstLetter(unitName, "");
        }
        //大营路
        if (unitName.contains(StreetEnum.DA_YING_LU_STREET.getName())) {
            catalogue = "daYingLu/picture/";
            unitNameChar = PinyinUtil.getPinyin(unitName, "");
        }

        //获取文件夹完整路径
        String path = CommonFormOfficeUtils.getTemplatePathByOS(catalogue);

        List<File> fileList = cn.hutool.core.io.FileUtil.loopFiles(path);
        for (File file : fileList) {
            String fileName = file.getName().substring(0, file.getName().lastIndexOf("."));
            if (unitNameChar.contains(fileName)) {
                //返回图片完整路径
                return CommonFormOfficeUtils.getTemplatePathByOS(catalogue + file.getName());
            }
        }
        return null;
    }

    /**
     * 获取本地照片的完整路径
     * @param unitName
     * @return
     */
    public static String getNewLocalImagePath(String unitName) {
        String catalogue = null;

        //获取中文首字母
        String unitNameChar = null;

        //威清门街道
        if (unitName.contains(StreetEnum.WEI_QING_MEN_STREET.getName())) {
            catalogue = "sqfwV2/weiqingmen/picture/";
            unitNameChar = PinyinUtil.getFirstLetter(unitName, "");
        }

        //获取文件夹完整路径
        String path = CommonFormOfficeUtils.getTemplatePathByOS(catalogue);

        List<File> fileList = cn.hutool.core.io.FileUtil.loopFiles(path);
        for (File file : fileList) {
            String fileName = file.getName().substring(0, file.getName().lastIndexOf("."));
            if (unitNameChar.contains(fileName)) {
                //返回图片完整路径
                return CommonFormOfficeUtils.getTemplatePathByOS(catalogue + file.getName());
            }
        }
        return null;
    }

    /**
     * 根据指定大小压缩图片
     *
     * @param bufferedImage  源图片
     * @param desFileSize 指定图片大小，单位kb
     * @param type 图片类型（png,jpg...）
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(BufferedImage bufferedImage, long desFileSize, String type) {
        ByteArrayOutputStream outStream =new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, type, outStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        byte[] imageBytes = outStream.toByteArray();

        if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return imageBytes;
        }

        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
        try {
            while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            log.info("图片原大小={}kb | 压缩后大小={}kb",
                    srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
        } catch (Exception e) {
            log.error("【图片压缩】msg=图片压缩失败!", e);
        }
        return imageBytes;
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < NINE_ZERO_ZERO) {
            accuracy = ZERO_EIGHT_FIVE;
        } else if (size < TWO_ZERO_FOUR_SEVEN) {
            accuracy = ZERO_SIX;
        } else if (size < THREE_TWO_SEVEN_FIVE) {
            accuracy = ZERO_FOUR_FOUR;
        } else {
            accuracy = ZERO_FOUR;
        }
        return accuracy;
    }

    public static byte[] bufferedImageToByte(BufferedImage bufferedImage, String type) {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, type, outStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        byte[] imageBytes = outStream.toByteArray();
        return imageBytes;
    }

    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param desFileSize 指定图片大小，单位kb
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) {
        if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
        try {
            while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            log.info("图片原大小={}kb | 压缩后大小={}kb",
                    srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
        } catch (Exception e) {
            log.error("【图片压缩】msg=图片压缩失败!", e);
        }
        return imageBytes;
    }

    /**
     * 并发下载图片
     * @param clazz 图片处理标识类
     * @param dataList 填充到excel表的数据
     * @return
     */
    public static List<Map<String, Object>> concurrenceDownImg(List<Map<String, Object>> dataList, Class<?> clazz) {
        // 提取所有图片
        List<Map<String, String>> imgList = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Map<String, Object> map : dataList) {
            Map<String, String> urlMap = new HashMap<>();
            for (Field field : fields) {
                if (map.get(field.getName()) != null) {
                    urlMap.put(field.getName(), map.get(field.getName()).toString());
                }
            }
            imgList.add(urlMap);
        }

        // 统计需要下载的图片数据量
        int taskCount = 0;
        for (Map<String, String> map : imgList) {
            for (String key : map.keySet()) {
                String urls[] = map.get(key).split(",");
                for (int i = 0; i < urls.length; i++) {
                    taskCount ++;
                }
            }
        }

        // 任务总数，制定计数器，用于判断此次提交的线程是否全部执行完毕
        // 单次计数器，用来判断当前线程组是否执行完毕
        CountDownLatch countDownLatch = new CountDownLatch(taskCount); // ①

        // 这里必须要用ConcurrentHashMap来保证现场安全，因为byteMap是在多线程里面操作的(ConcurrentHashMap不能设置null值)
        Map<String, byte[]> byteMap = new ConcurrentHashMap<>();

        // 添加任务
        for (int k = 0; k < imgList.size(); k ++) {
            Map<String, String> map = imgList.get(k);
            for (String key : map.keySet()) {
                String  urls [] = map.get(key).split(",");
                for (int i = 0; i < urls.length ; i ++) {
                    int finalI = i;
                    int finalK = k;
                    ThreadPoolExecutorUtil.getInstance().submit(new Runnable() {
                        @Override
                        public void run() {
                            String url = urls[finalI];
                            byte[] imgByte = PictureUtils.scaleImageToOneByte(url, false);
                            byteMap.put(key + "&" + finalK + "&" + finalI, imgByte);
                            // 线程执行完，计数器 -1
                            countDownLatch.countDown();  // ②
                        }
                    });
                }
            }
        }

        try {
            // 阻塞等待线程池任务执行完(最多等待120秒)
            countDownLatch.await(120, TimeUnit.SECONDS);  // ③
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程池执行完-------------------------------------

        // 图片归类
        Map<String, List<byte[]>> bytMap = new HashMap<>();
        for (String key : byteMap.keySet()) {
            String k = key.substring(0, key.lastIndexOf("&"));
            if (bytMap.containsKey(k)) {
                if (byteMap.get(key) != null && byteMap.get(key).length > 0) {
                    bytMap.get(k).add(byteMap.get(key));
                } else {
                    bytMap.get(k).addAll(Lists.newArrayList());
                }
            } else {
                List<byte[]> list = new ArrayList<>();
                if (byteMap.get(key) != null && byteMap.get(key).length > 0) {
                    list.add(byteMap.get(key));
                } else {
                    list.addAll(Lists.newArrayList());
                }
                bytMap.put(k, list);
            }
        }

        // 把图片填充到数据列表里
        for (String key : bytMap.keySet()) {
            int i = Integer.parseInt(key.substring(key.lastIndexOf("&") + 1));
            String k = key.substring(0, key.lastIndexOf("&"));
            dataList.get(i).put(k, bytMap.get(key));
        }

        return dataList;
    }

    public static byte[] scaleImageToOneByte(String imgPath, boolean isRotate) {
        if (StringUtils.isBlank(imgPath)) return new byte[0];
        InputStream inputStream = null;
        try {
            String url = imgPath;
            inputStream = CommonFormOfficeForAdminUtils.downloadPictureCache(url);
            if (inputStream == null) return new byte[0];
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            //如果是横图，就旋转90度
            if (bufferedImage.getWidth() > bufferedImage.getHeight() && isRotate) {
                bufferedImage = (BufferedImage) ImgUtil.rotate(bufferedImage, 90);
            }

            // 获取图片后缀
            String formatName = url.substring(url.lastIndexOf(".") + 1);
            formatName = "jpg".equals(formatName) ? "png" : formatName;

            // 不压缩图片
            byte[] imgBytes = bufferedImageToByte(bufferedImage, formatName);

            // 图片压缩
            //byte[] imgBytes = compressPicForScale(bufferedImage, 100, formatName);

            return imgBytes;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return new byte[0];
    }

    /**
     * 获取url指定长度
     * @param urls
     * @param picNum 显示的图片张数
     * @return
     */
    public static String splitUrl(List<String> urls, int picNum) {
        StringBuilder stringBuilder = new StringBuilder();
        int num = 0;
        for (String u : urls) {
            if (StringUtils.isBlank(u)) continue;
            if (num < picNum) {
                stringBuilder.append(u);
            }
            num ++;
            if (num < picNum) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 获取url指定长度
     * @param url
     * @param picNum 显示的图片张数
     * @return
     */
    public static String splitUrl(String url, int picNum) {
        if (StringUtils.isBlank(url)) return url;
        String urls [] = url.split(",");
        StringBuilder stringBuilder = new StringBuilder();
        int num = 0;
        for (String u : urls) {
            if (num < picNum) {
                stringBuilder.append(u);
            }
            num ++;
            if (num < picNum) {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }
}
